# Copyright (c) 2016 SolarWinds, LLC.
# All rights reserved.

module TraceView
  module Inst
    module TyphoeusRequestOps

      def self.included(klass)
        ::TraceView::Util.method_alias(klass, :run, ::Typhoeus::Request::Operations)
      end

      def run_with_traceview
        return run_without_traceview unless TraceView.tracing?

        TraceView::API.log_entry(:typhoeus)

        # Prepare X-Trace header handling
        blacklisted = TraceView::API.blacklisted?(url)
        context = TraceView::Context.toString
        task_id = TraceView::XTrace.task_id(context)
        options[:headers]['X-Trace'] = context unless blacklisted

        response = run_without_traceview

        if response.code == 0
          TraceView::API.log(:typhoeus, :error, { :ErrorClass => response.return_code,
                                                  :ErrorMsg => response.return_message })
        end

        kvs = {}
        kvs[:IsService] = 1
        kvs[:HTTPStatus] = response.code
        kvs[:Backtrace] = TraceView::API.backtrace if TraceView::Config[:typhoeus][:collect_backtraces]

        uri = URI(response.effective_url)

        # Conditionally log query params
        if TraceView::Config[:typhoeus][:log_args]
          kvs[:RemoteURL] = uri.to_s
        else
          kvs[:RemoteURL] = uri.to_s.split('?').first
        end

        kvs[:HTTPMethod] = ::TraceView::Util.upcase(options[:method])
        kvs[:Blacklisted] = true if blacklisted

        # Re-attach net::http edge unless it's blacklisted or if we don't have a
        # valid X-Trace header
        unless blacklisted
          xtrace = response.headers['X-Trace']

          if xtrace && TraceView::XTrace.valid?(xtrace) && TraceView.tracing?

            # Assure that we received back a valid X-Trace with the same task_id
            if task_id == TraceView::XTrace.task_id(xtrace)
              TraceView::Context.fromString(xtrace)
            else
              TraceView.logger.debug "Mismatched returned X-Trace ID: #{xtrace}"
            end
          end
        end

        TraceView::API.log(:typhoeus, :info, kvs)
        response
      rescue => e
        TraceView::API.log_exception(:typhoeus, e)
        raise e
      ensure
        TraceView::API.log_exit(:typhoeus)
      end
    end

    module TyphoeusHydraRunnable
      def self.included(klass)
        ::TraceView::Util.method_alias(klass, :run, ::Typhoeus::Hydra)
      end

      def run_with_traceview
        kvs = {}

        kvs[:queued_requests] = queued_requests.count
        kvs[:max_concurrency] = max_concurrency

        # FIXME: Until we figure out a strategy to deal with libcurl internal
        # threading and Ethon's use of easy handles, here we just do a simple
        # trace of the hydra run.
        TraceView::API.trace(:typhoeus_hydra, kvs) do
          run_without_traceview
        end
      end
    end

  end
end

if TraceView::Config[:typhoeus][:enabled]
  if defined?(::Typhoeus)
    TraceView.logger.info '[traceview/loading] Instrumenting typhoeus' if TraceView::Config[:verbose]
    ::TraceView::Util.send_include(::Typhoeus::Request::Operations, ::TraceView::Inst::TyphoeusRequestOps)
    ::TraceView::Util.send_include(::Typhoeus::Hydra, ::TraceView::Inst::TyphoeusHydraRunnable)
  end
end
